﻿#region Copyright Statement
//  ------------------------------------------------------------------------------
//  
//    This file is part of The Streambolics Library.
//    Copyright © 2005-2009, Stephan Leclercq & Streambolics
//    All rights reserved.
//  
//    The Streambolics Library is free software; you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation; either version 2 of the License, or
//    (at your option) any later version.
//  
//    The Streambolics Library is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//  
//    You should have received a copy of the GNU General Public License
//    along with Streambolics; if not, write to the Free Software
//    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
//  
//  ------------------------------------------------------------------------------
#endregion

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Streambolics.Hp41s
{
    /// <summary>
    ///     A linear list of steps, constituting a program or a subroutine.
    /// </summary>
    /// <remarks><para>
    ///     According to HP-41C standards, every program ends with an <c>END</c>
    ///     instruction. This class ensures that the <c>END</c> instruction is 
    ///     always present at the end of the steps.
    /// </para><para>
    ///     Instructions are numbered. Again, according to HP-41C standard, the
    ///     first instruction is numbered <c>001</c>. This line number is arbitrary,
    ///     as user programs do not refer to instructions by their line number (they
    ///     are using labels instead.)
    /// </para></remarks>

    public class Steps : Data
    {
        private List<Instruction> _Instructions = new List<Instruction> ();

        public Steps ()
            : base ("Program")
        {
        }

        public Steps (params Instruction[] aInstructions)
            : this ()
        {
            Append (aInstructions);
        }

        public Instruction this[int i]
        {
            get
            {
                --i;
                if (i < 0 || i > _Instructions.Count)
                {
                    throw new Exception ("NONEXISTENT");
                }
                else if (i == _Instructions.Count)
                {
                    return CommonInstructions.End;
                }
                else
                {
                    return _Instructions[i];
                }
            }
        }

        public IEnumerable<Instruction> Instructions
        {
            get
            {
                return _Instructions;
            }
        }

        public void Insert (Instruction aInstruction, int aPosition)
        {
            _Instructions.Insert (aPosition - 1, aInstruction);
        }

        public void Append (params Instruction[] aInstructions)
        {
            _Instructions.AddRange (aInstructions);
        }

        public string LongListing (int aProgramCounter)
        {
            StringBuilder sb = new StringBuilder ();
            for (int i = 1; i <= _Instructions.Count + 1; i++)
            {
                sb.AppendFormat ("{0}{1:D3}{2}{3}\r\n", this[i].IsLabel () ? '♦' : ' ', i, i == aProgramCounter ? '►' : ' ', this[i]);
            }
            return sb.ToString ();
        }

        public string CompactListing ()
        {
            StringBuilder sb = new StringBuilder ();
            bool prevIsLabel = true;
            int curcount = 0;
            for (int i = 1; i <= _Instructions.Count + 1; i++)
            {
                Instruction inst = this[i];

                if (inst.IsLabel ())
                {
                    if (curcount > 0)
                    {
                        sb.AppendLine ();
                    }
                    if (!prevIsLabel)
                    {
                        sb.AppendLine ();
                    }
                    sb.AppendFormat ("♦{0:D3} {1}", i, inst);
                    sb.AppendLine ();
                    curcount = 0;
                }
                else
                {
                    if (prevIsLabel)
                    {
                        curcount = 0;
                        sb.AppendLine ();
                    }

                    string s = inst.ToString ();

                    if (curcount > 0 && curcount + s.Length > 32)
                    {
                        sb.AppendLine ();
                        curcount = 0;
                    }

                    if (curcount == 0)
                    {
                        sb.Append ("     ");
                        curcount = 5;
                    }
                    else
                    {
                        sb.Append ("  ");
                        curcount += 2;
                    }
                    sb.Append (s);
                    curcount += s.Length;
                }
                prevIsLabel = inst.IsLabel ();
            }

            if (curcount > 0)
            {
                sb.AppendLine ();
            }
            return sb.ToString ();
        }

        public override string ToString ()
        {
            return LongListing (0);
        }
    }
}
